package com.shujia.jinjie

import scala.io.Source

object HomeWork {
  /**
   * 创建几个样例类，为封装学生，成绩，科目打下基础
   */
  private case class Student(id: Long, name: String, age: Int, gender: String, clazz: String)

  private case class Score(id: Long, subject_id: Long, score: Int)

  private case class Subject(subject_id: Long, subject_name: String, subject_score: Int)

  /**
   * 创建几个List集合，来存储这些对象
   */
  private var studentsList: List[Student] = _
  private var scoreList: List[Score] = _
  private var subjectList: List[Subject] = _

  /**
   * 创建几个Map集合，为后面关联打下基础
   */
  // 学号-【姓名，班级】
  private var idWithNameClazzMap: Map[Long, String] = _
  //科目编号-【科目名字】
  private var subjectIdWithSubjectNameMap: Map[Long, String] = _
  //科目编号-【科目总分】
  private var subjectIdWithSubjectScoreMap: Map[Long, Int] = _

  /**
   * 定义一个函数，加载数据，将上面所有的变量赋值，为后续的需求编写做准备
   */
  private def loadData(): Unit = {
    //初始化studentsList
    studentsList = "scala/data/students.txt".load().map {
      case Array(id: String, name: String, age: String, gender: String, clazz: String) => Student(id.toLong, name, age.toInt, gender, clazz)
    }

    //初始化scoreList
    scoreList = "scala/data/score.txt".load().map {
      case Array(id: String, subject_id: String, score: String) => Score(id.toLong, subject_id.toLong, score.toInt)
    }

    //初始化subjectList
    subjectList = "scala/data/subject.txt".load().map {
      case Array(subject_id: String, subject_name: String, subject_score: String) => Subject(subject_id.toLong, subject_name, subject_score.toInt)
    }


    //初始化idWithNameClazzMap
    idWithNameClazzMap = studentsList.map((stu: Student) => (stu.id, stu.name + "," + stu.clazz)).toMap
    //初始化subjectIdWithSubjectNameMap
    subjectIdWithSubjectNameMap = subjectList.map((subject: Subject) => (subject.subject_id, subject.subject_name)).toMap
    //初始化subjectIdWithSubjectScoreMap
    subjectIdWithSubjectScoreMap = subjectList.map((subject: Subject) => (subject.subject_id, subject.subject_score)).toMap
  }


  /**
   * 统计班级人数 [班级,人数]
   */
  private def xuQiu1(): Unit = {
    studentsList.groupBy((stu: Student) => stu.clazz) // 编写分组条件
      .map((kv: (String, List[Student])) => s"[${kv._1},${kv._2.size}]").foreach(println)
  }

  /**
   * 统计学生的总分 [学号,学生姓名,学生年龄,总分]
   * (1500100312,List(Score(1500100312,1000001,119), Score(1500100312,1000002,25), Score(1500100312,1000003,96), Score(1500100312,1000004,75), Score(1500100312,1000005,39), Score(1500100312,1000006,93)))
   * (1500100312,List(119,25,96,75,39,93))
   * (1500100312,xxx)
   */
  private def xuQiu2(): Unit = {
    //scoreList
    val idWithSumScoreMap: Map[Long, Int] = scoreList.groupBy((sco: Score) => sco.id) //根据学号进行分组
      .map((kv: (Long, List[Score])) => {
        (kv._1, kv._2.map(_.score).sum)
      })

    studentsList.map((stu: Student) => {
      s"[${stu.id}, ${stu.name}, ${stu.age}, ${idWithSumScoreMap.getOrElse(stu.id, 0)}]"
    }).foreach(println)
  }

  /**
   *  统计年级排名前十学生各科的分数 [学号, 姓名，班级，科目，分数]
   */
  private def xuQiu3():Unit={
    val ids: List[Long] = scoreList.groupBy((sco: Score) => sco.id) // 按照学号进行分组
      .map((kv: (Long, List[Score])) => {
        (kv._1, kv._2.map(_.score).sum) //求出每个学生的总分
      })
      .toList // 将结果转成List集合
      .sortBy(-_._2) // 按照总分降序排序
      .take(10)
      .map(_._1) //单独将10个人的学号取出来放到集合中

    //得到学生的姓名和班级
    val top10StuMap: Map[Long, String] = idWithNameClazzMap.filter((kv: (Long, String)) => ids.contains(kv._1))

    //得到学生的科目编号和成绩
    scoreList.filter((sco:Score)=>ids.contains(sco.id))
      .map((sco:Score)=>{
        val subjectName: String = subjectIdWithSubjectNameMap.getOrElse(sco.subject_id, "暂无对应科目")
        val nameWithClass: String = top10StuMap.getOrElse(sco.id, "暂无该学生")
        s"[${sco.id},${nameWithClass},${subjectName},${sco.score}]"
      }).foreach(println)
  }

  /**
   * 统计总分大于年级平均分的学生 [学号，姓名，班级，总分]
   */

  private def xuQiu4():Unit={
    //计算年级平均分(将所有人的分数加起来，除以人数)
    //372
    val avgScore: Int = scoreList.map(_.score).sum / studentsList.size


    scoreList.groupBy((sco: Score) => sco.id) // 按照学号进行分组
      .map((kv: (Long, List[Score])) => {
        (kv._1, kv._2.map(_.score).sum) //求出每个学生的总分
      })
      .filter(_._2>avgScore) //过滤出总分大于年级平均分的学生
      .map((kv:(Long,Int))=>{
        //根据学号查询出姓名和班级
        val nameWithClass: String = idWithNameClazzMap.getOrElse(kv._1, "暂无该学生")
        s"[${kv._1},${nameWithClass},${kv._2}]"
      }).foreach(println)
  }

  /**
   * 统计每科都及格的学生 [学号，姓名，班级，科目，分数]
   */
  private def xuQiu5():Unit={
    //在分数基础上，过滤出及格的学生
    scoreList.filter((sco:Score)=>{
      val jiGeXian: Double = subjectIdWithSubjectScoreMap.getOrElse(sco.subject_id, -1) * 0.6
      sco.score >= jiGeXian
    }).groupBy(_.id) //根据学号分组
      .filter(_._2.size==6) // 过滤出6门课都及格的学生
      .flatMap(_._2)
      .map((sco:Score)=>{
        val nameWithClass: String = idWithNameClazzMap.getOrElse(sco.id, "暂无该学生")
        val subjectName: String = subjectIdWithSubjectNameMap.getOrElse(sco.subject_id, "暂无科目")
        s"[${sco.id},${nameWithClass},${subjectName},${sco.score}]"
      }).foreach(println)
  }

  /**
   * 统计每个班级的前三名 [学号，姓名，班级，分数]
   */
  private def xuQiu6():Unit={
    val ids: List[Long] = scoreList.groupBy((sco: Score) => sco.id) // 按照学号进行分组
      .map((kv: (Long, List[Score])) => {
        //获取该学生的姓名和班级
        val nameWithClass: String = idWithNameClazzMap.getOrElse(kv._1, "暂无该学生")
        val info: Array[String] = nameWithClass.split(",")
        val name: String = info(0)
        val clazz: String = info(1)
        (kv._1, name, clazz, kv._2.map(_.score).sum) //求出每个学生的总分
      }).groupBy(_._3) //根据班级进行分组
      .flatMap((kv: (String, Iterable[(Long, String, String, Int)])) => {
        kv._2.toList.sortBy(-_._4).take(3).map(_._1)
      }).toList


    scoreList.filter((sco:Score)=>ids.contains(sco.id))
      .map((sco:Score)=>{
        val nameWithClass: String = idWithNameClazzMap.getOrElse(sco.id, "暂无该学生")
        s"[${sco.id},${nameWithClass},${sco.score}]"
      }).foreach(println)
  }

  /**
   *  统计偏科最严重的前100名学生  [学号，姓名，班级，科目，分数]  利用方差去判断
   *
   *  方差：检测数据的离散程度
   *  方差的结果越大，表示离散程度越大，偏科越严重；反之，结果越小，离散程度越小，越稳定。
   */

  private def xuQiu7():Unit={
    //将分数进行归一化，因为每门课的总分是不一样的，不能直接取值求平均，将所有的分数统一到1-100之间
    val ids: List[Long] = scoreList.map((sco: Score) => Score(sco.id, sco.subject_id, sco.score * 100 / subjectIdWithSubjectScoreMap.getOrElse(sco.subject_id, -1)))
      .groupBy(_.id) //根据学生学号进行分组
      .map((kv: (Long, List[Score])) => {
        val id: Long = kv._1
        val stuScoreList: List[Score] = kv._2
        //计算这个学生的平均分
        val stuAvgScore: Int = stuScoreList.map(_.score).sum / kv._2.size
        val fc: Double = stuScoreList.map((s: Score) => Math.pow(s.score - stuAvgScore, 2)).sum / stuScoreList.size
        (id, fc)
      }).toList
      .sortBy(-_._2)
      .take(100)
      .map(_._1)

    //得到学生的姓名和班级
    val top10StuMap: Map[Long, String] = idWithNameClazzMap.filter((kv: (Long, String)) => ids.contains(kv._1))

    //得到学生的科目编号和成绩
    scoreList.filter((sco:Score)=>ids.contains(sco.id))
      .map((sco:Score)=>{
        val subjectName: String = subjectIdWithSubjectNameMap.getOrElse(sco.subject_id, "暂无对应科目")
        val nameWithClass: String = top10StuMap.getOrElse(sco.id, "暂无该学生")
        s"[${sco.id},${nameWithClass},${subjectName},${sco.score}]"
      }).foreach(println)

  }

  /**
   * 编写一个隐式转换类，直接使用文件路径获取对应集合
   */
  implicit class LoadDataClass(path: String) {
    def load(): List[Array[String]] = {
      Source.fromFile(path)
        .getLines()
        .toList
        .map((e: String) => e.split(","))
    }
  }


  def main(args: Array[String]): Unit = {
    //加载数据，初始化变量
    loadData()
    //    xuQiu1()
//    xuQiu2()
//    xuQiu3()
//    xuQiu4()

//    xuQiu5()
//  xuQiu6()
    xuQiu7()


  }
}
